﻿//#define NGUI
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
using System.IO;
using System.Text.RegularExpressions;
using UnityEngine.UI;
using System.Linq;

[CustomEditor(typeof(MonoAutoQuote), true)]
public class AutoQuoteEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        if (GUILayout.Button("关联子节点引用"))
        {
            Component c = target as Component;
            if (c != null)
                AutoQuoteMenu.AutoSetRef(c, c.gameObject);
        }
    }
}
[CustomEditor(typeof(MonoAutoQuote), true)]
public class AutoQuoteEditorWindow : EditorWindow
{
    public static bool mAutoRef = true;
    public static bool mAutoCodeRef = false;
    public static bool mFindEnable = false;
    public static bool mEventEnable = false;
    public static bool mFuncEnable = false;
    public static bool mAutoCodeEvent = false;
    public static bool mAutoCodeFunc = false;
    public static bool mDebugMode = false;

    private bool mIndependent = true;

    public void Awake()
    {
        Get();
    }

    private void Get()
    {
        if (PlayerPrefs.HasKey("mAutoRef"))
            mAutoRef = PlayerPrefs.GetInt("mAutoRef") > 0 ? true : false;
        if (PlayerPrefs.HasKey("mAutoCodeRef"))
            mAutoCodeRef = PlayerPrefs.GetInt("mAutoCodeRef") > 0 ? true : false;
        if (PlayerPrefs.HasKey("mFindEnable"))
            mFindEnable = PlayerPrefs.GetInt("mFindEnable") > 0 ? true : false;
        if (PlayerPrefs.HasKey("mEventEnable"))
            mEventEnable = PlayerPrefs.GetInt("mEventEnable") > 0 ? true : false;
        if (PlayerPrefs.HasKey("mAutoCodeEvent"))
            mAutoCodeEvent = PlayerPrefs.GetInt("mAutoCodeEvent") > 0 ? true : false;
        if (PlayerPrefs.HasKey("mFuncEnable"))
            mAutoCodeEvent = PlayerPrefs.GetInt("mFuncEnable") > 0 ? true : false;
          if (PlayerPrefs.HasKey("mAutoCodeFunc"))
            mAutoCodeEvent = PlayerPrefs.GetInt("mAutoCodeFunc") > 0 ? true : false;
        if (PlayerPrefs.HasKey("IsNGUI"))
            AutoGenerateSet.IsNGUI = PlayerPrefs.GetInt("IsNGUI") > 0 ? true : false;

        if (PlayerPrefs.HasKey("CSFilePath"))
            AutoGenerateSet.CSFilePath = PlayerPrefs.GetString("CSFilePath");
        if (PlayerPrefs.HasKey("FieldStartM"))
            AutoGenerateSet.FieldStartM = PlayerPrefs.GetString("FieldStartM");







    }

    private void Save()
    {
        PlayerPrefs.SetInt("mAutoRef", mAutoRef ? 1 : 0);
        PlayerPrefs.SetInt("mAutoCodeRef", mAutoCodeRef ? 1 : 0);
        PlayerPrefs.SetInt("mFindEnable", mFindEnable ? 1 : 0);
        PlayerPrefs.SetInt("mEventEnable", mEventEnable ? 1 : 0);
        PlayerPrefs.SetInt("mFuncEnable", mFuncEnable ? 1 : 0);

        PlayerPrefs.SetInt("mAutoCodeEvent", mAutoCodeEvent ? 1 : 0);
        PlayerPrefs.SetInt("mAutoCodeFunc", mAutoCodeFunc ? 1 : 0);
        PlayerPrefs.SetInt("IsNGUI", AutoGenerateSet.IsNGUI ? 1 : 0);
        PlayerPrefs.SetString("CSFilePath", AutoGenerateSet.CSFilePath);
        PlayerPrefs.SetString("FieldStartM", AutoGenerateSet.FieldStartM);

        PlayerPrefs.Save();
    }

    public void OnDestroy()
    {
        Save();
    }

    public void OnGUI()
    {
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("加载配置"))
        {
            Get();
        }
        if (GUILayout.Button("保存配置"))
        {
            Save();
        }
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        mIndependent = GUILayout.Toggle(mIndependent, "单独生成一个类");
        GUILayout.EndHorizontal();

        if (mIndependent)
        {
            GUILayout.Label("生成代码路径");
            AutoGenerateSet.CSFilePath = GUILayout.TextField(AutoGenerateSet.CSFilePath);
            AutoGenerateSet.IsNGUI = GUILayout.Toggle(AutoGenerateSet.IsNGUI, "Yes NGUI,No UGUI");
            GUILayout.Label("字段前缀");
            AutoGenerateSet.FieldStartM = GUILayout.TextField(AutoGenerateSet.FieldStartM);

            if (GUILayout.Button("生成一个分布类"))
            {
                Save();
                AutoQuoteMenu.AutoCreateCodeMenu();
            }
        }
        else
        {
            mAutoRef = GUILayout.Toggle(mAutoRef, "自动设置组件的值");
            mAutoCodeRef = GUILayout.Toggle(mAutoCodeRef, "生成Find代码");
            mAutoCodeEvent = GUILayout.Toggle(mAutoCodeEvent, "生成Event注册");
            mAutoCodeFunc = GUILayout.Toggle(mAutoCodeFunc, "生成Event函数");
            mFindEnable = GUILayout.Toggle(mFindEnable, "Find注释");
            mEventEnable = GUILayout.Toggle(mEventEnable, "Event注释");
            mFuncEnable = GUILayout.Toggle(mFuncEnable, "Event函数注释");
            mDebugMode = GUILayout.Toggle(mDebugMode, "不生成代码文件");

            if (GUILayout.Button("选中物体生成引用"))
            {
                Save();
                AutoQuoteMenu.SetRefMenu();
            }

        }



    }
}

public static class AutoGenerateSet
{
    public static string CSFilePath = Application.dataPath + "/Script/UI/UITemplateFile";
    public static bool IsNGUI;
    public static string FieldStartM = "m";
}

public partial class AutoQuoteMenu
{
    private static string mUGUIButtonEvent = "{0}.{1}.AddListener(delegate {{ {2}({3}); }});";
    private static string mEventPrefix = "OnClick";
    private static string result = "";
    private static string mFrontPrefix = "#";


    //生成代码相关的
    private static string mUGUIAryEvent =
        @"for (int i = 0; i < {0}.{1}; i++)
        {{
            {2}[i].{3}.AddListener(delegate {{ {4}({5}[i]); }});
        }}";



    //第二种方式生成函数
    private static string mGTwoButtonEvent = @"
        delRegist = new EventDelegate(this, " + "\"{0}\"" + @");
        delRegist.parameters[0].obj = {1}.gameObject.GetComponent<{2}>();
        {3}.{4}.Add(delRegist);";
    

    private static string mGTwoAryEvent =
        @"for (int i = 0; i < {0}.{1}; i++)
        {{
            delRegist = new EventDelegate(this," + "\"{2}\"" + @");
            delRegist.parameters[0].obj = {3}[i].gameObject.GetComponent<{4}>();
            {5}[i].{6}.Add(delRegist);
        }}";

    public static string classTemp =
    @"
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        using UnityEngine.UI;
        public partial class //@ClassName : UIObject
        {
            //@Field

            partial void InitCustom();
            public override void Init()
            {
                //@Init
                InitCustom();
            }
            
            //@Func

        }
        ";



    public static void AutoSetRef(Component c, GameObject o, bool clear = false)
    {
        System.Type t = c.GetType();
        var infoList = t.GetFields(BindingFlags.Public | BindingFlags.Instance);
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        string name = string.Empty;
        System.Text.StringBuilder errorText = new System.Text.StringBuilder();

        foreach (var item in infoList)
        {
            var fieldType = item.FieldType;
            string fieldName = item.Name;
            System.Type aryType = null;
            if (IsSubClass<MonoBehaviour>(fieldType) || IsSubClass<GameObject>(fieldType) || IsSubClass<IEnumerable>(fieldType))
            {
                if (item.Name.StartsWith("m"))
                    name = mFrontPrefix + item.Name.Substring(1);
                else
                    name = mFrontPrefix + item.Name;

                if (fieldType.IsArray)
                    name = name + "_" + fieldType.Name + "#" + "Ary";
                else if (IsSubClass<IEnumerable>(fieldType))
                {
                    aryType = fieldType.GetGenericArguments()[0];
                    name = name + "_" + aryType.Name + "#List";
                }

                Transform tr = FindEnd(o.transform, name);
                if (tr == null)
                {
                    errorText.AppendLine(c.name + "中" + name + "引用没找到引用");
                    continue;
                }

                object val = null;
                if (clear == false)
                {
                    IEnumerable list = null;
                    if (fieldType.IsArray || IsSubClass<IEnumerable>(fieldType))
                    {

                        Object[] ary = null;
                        int count = 0;
                        if (IsSubClass<IEnumerable>(fieldType))
                            ary = tr.GetComponentsInChildren(aryType);
                        else
                            ary = tr.GetComponentsInChildren(fieldType);

                        if (ary != null)
                            count = ary.Length;

                        list = GetIE(fieldType, ary, count);
                        val = list;
                    }
                    else if (IsSubClass<GameObject>(fieldType))
                        val = tr.gameObject;
                    else if (IsSubClass<MonoBehaviour>(fieldType))
                        val = tr.GetComponent(fieldType);
                }

                try
                {
                    item.SetValue(c, val);
                }
                catch (System.Exception ex)
                {
                    Debug.Log(ex.Message);
                }

            }
        }

        if (errorText.Length > 0)
            Debug.LogError(errorText);
    }

    public static void AutoCreateCode(GameObject o)
    {
        string goName = o.name;
        var infoList = o.GetComponentsInChildren<Transform>(true);
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        string name = string.Empty;
        System.Text.StringBuilder fieldText = new System.Text.StringBuilder();
        System.Text.StringBuilder eventText = new System.Text.StringBuilder();
        System.Text.StringBuilder errorText = new System.Text.StringBuilder();
        System.Text.StringBuilder funcText = new System.Text.StringBuilder();
        System.Text.StringBuilder registerText = new System.Text.StringBuilder();

        string nodeName = null;
        string field = null;
        int twoWayGenerateIndex = 0;
        foreach (var item in infoList)
        {
            if (item.gameObject.name.StartsWith("#"))
            {
                nodeName = item.gameObject.name.TrimStart('#');
                if (nodeName.Contains("_"))
                {
                    nodeName = nodeName.Substring(0, nodeName.IndexOf("_"));
                }

                var g = GetGenerateInfo(item);
                field = g.Field;
                string fieldName = AutoGenerateSet.FieldStartM + nodeName;

                if (string.IsNullOrEmpty(field))
                    continue;

                fieldText.AppendLine(string.Format(g.FieldText, field, fieldName));

                if (g.IsGenerateEvent)
                {
                    eventText.AppendLine(string.Format("public event System.Action<{0}> On{1};", field, nodeName));
                    funcText.AppendLine(string.Format("public void " + mEventPrefix + "{0}({1} go)", nodeName, field) +
                        @"{
                        if(null != On" + nodeName + @")
                        {
                            On" + nodeName + @"(go);
                        }
                    }
                    ");
                }

                string registerStr = string.Empty;
                string methodName = mEventPrefix + nodeName;


                if (g.IsArry || g.IsList)
                {
                    if (AutoGenerateSet.IsNGUI)
                        registerStr = string.Format(mGTwoAryEvent, fieldName, g.LenStr, methodName, fieldName, field, fieldName, g.EventStr);
                    else
                        registerStr = string.Format(mUGUIAryEvent, fieldName, g.LenStr, fieldName, g.EventStr, methodName, fieldName);
                }
                else
                {
                    switch (field)
                    {
                        case "UIInput":
                        case "UIButton":
                        case "UIToggle":
                            registerStr = string.Format(mGTwoButtonEvent, methodName, fieldName, field, fieldName, g.EventStr);
                            break;
                        case "Button":
                        case "Toggle":
                            registerStr = string.Format(mUGUIButtonEvent, fieldName, g.EventStr, methodName, fieldName);
                            break;
                    }
                }

                if (twoWayGenerateIndex == 0 && AutoGenerateSet.IsNGUI)
                    registerText.AppendLine("EventDelegate delRegist = null;");

                twoWayGenerateIndex++;

                if (string.IsNullOrEmpty(registerStr) == false)
                {
                    registerText.AppendLine(registerStr);
                }
            }
        }

        string className = goName + "Object";
        string fileName = goName + "ObjectA.cs";
        string filePath = AutoGenerateSet.CSFilePath + "/" + fileName;
        string temp = classTemp;

        temp = temp.Replace("//@Field", fieldText.ToString() + "\n" + eventText.ToString());
        temp = temp.Replace("//@Init", registerText.ToString());
        temp = temp.Replace("//@Func", funcText.ToString());
        temp = temp.Replace("//@ClassName", className);
        File.WriteAllText(filePath, temp, System.Text.Encoding.UTF8);
        Debug.Log("输出成功" + filePath);
        AssetDatabase.Refresh();
    }


    public static GenerateInfo GetGenerateInfo(Transform item)
    {
        GenerateInfo g = new GenerateInfo();

        //数据和list情况
        string aryType = null;
        if (item.name.Contains("_"))
        {
            try
            {
                aryType = item.name.Substring(item.name.LastIndexOf("_") + 1);
                string[] ary = aryType.Split('#');
                g.Field = ary[0];
                if (ary[1] == "List")
                {
                    g.IsList = true;
                    g.LenStr = "Count";
                    g.FieldText = "public List<{0}> {1};";
                }
                else if (ary[1] == "Ary")
                {
                    g.IsArry = true;
                    g.LenStr = "Length";
                    g.FieldText = "public {0} [] {1};";
                }
            }
            catch (System.Exception ex)
            {
                Debug.LogError("格式错误: " + item.name + " 正确格式#变量名_类名#List");
                UnityEngine.Assertions.Assert.IsTrue(false);
            }

            IsGenerateEvent(g);
            return g;
        }


        if (AutoGenerateSet.IsNGUI)
        {
#if NGUI

            UIInput input = item.GetComponent<UIInput>();
            if (input != null && g.Field == null)
            {
                g.Field = "UIInput";
            }

            UIButton btn = item.GetComponent<UIButton>();
            if (btn != null)
            {
                g.Field = "UIButton";
            }

            UIProgressBar bar = item.GetComponent<UIProgressBar>();
            if (bar != null && g.Field == null)
            {
                g.Field = "UIProgressBar";
            }

            UISprite sprite = item.GetComponent<UISprite>();
            if (sprite != null && g.Field == null)
            {
                g.Field = "UISprite";
            }

            UILabel lab = item.GetComponent<UILabel>();
            if (lab != null && g.Field == null)
            {
                g.Field = "UILabel";
            }
#endif
        }
        else
        {

            Button ubtn = item.GetComponent<Button>();
            if (ubtn != null && g.Field == null)
            {
                g.Field = "Button";
            }

            Toggle toggle = item.GetComponent<Toggle>();
            if (toggle != null && g.Field == null)
            {
                g.Field = "Toggle";
            }

            Text text = item.GetComponent<Text>();
            if (text != null && g.Field == null)
            {
                g.Field = "Text";
            }

            Image image = item.GetComponent<Image>();
            if (image != null && g.Field == null)
            {
                g.Field = "Image";
            }
        }

        g.FieldText = "public {0} {1};";
        IsGenerateEvent(g);
        return g;
    }

    private static IEnumerable GetIE(System.Type fieldType, Object[] objList, int count)
    {
        System.Type aryType = null;
        if (fieldType.IsGenericType)
            aryType = fieldType.GetGenericArguments()[0];

        List<Object> listObject = new List<Object>();
        IEnumerable list = null;
        if (aryType != null && aryType.IsArray)
        {
            switch (aryType.Name)
            {
                case "UIButton":
                case "UISprite":
                case "UILabel":
                case "UIToggle":
                case "Button":
                case "Image":
                case "Text":
                case "Toggle":
                    return objList;
                case "GameObject":
                    for (int i = 0; i < objList.Length; i++)
                        listObject.Add((objList[i] as Component).gameObject);
                    return objList;
            }
        }
        else
        {
            switch (aryType.Name)
            {
#if NGUI
                case "UIButton":
                    List<UIButton> list1 = new List<UIButton>();
                    for (int i = 0; i < objList.Length; i++)
                        list1.Add(objList[i] as UIButton);
                    return list1;
                case "UISprite":
                    List<UISprite> list2 = new List<UISprite>();
                    for (int i = 0; i < objList.Length; i++)
                        list2.Add(objList[i] as UISprite);
                    return list2;
                case "UILabel":
                    List<UILabel> list3 = new List<UILabel>();
                    for (int i = 0; i < objList.Length; i++)
                        list3.Add(objList[i] as UILabel);
                    return list3;
                case "UIToggle":
                    List<UIToggle> list10 = new List<UIToggle>();
                    for (int i = 0; i < objList.Length; i++)
                        list10.Add(objList[i] as UIToggle);
                    return list10;
#endif
                case "Button":
                    List<Button> list4 = new List<Button>();
                    for (int i = 0; i < objList.Length; i++)
                        list4.Add(objList[i] as Button);
                    return list4;
                case "Image":
                    List<Image> list5 = new List<Image>();
                    for (int i = 0; i < objList.Length; i++)
                        list5.Add(objList[i] as Image);
                    return list5;
                case "Text":
                    List<Text> list6 = new List<Text>();
                    for (int i = 0; i < objList.Length; i++)
                        list6.Add(objList[i] as Text);
                    return list6;
                case "GameObject":
                    List<GameObject> list7 = new List<GameObject>();
                    for (int i = 0; i < objList.Length; i++)
                        list7.Add(((Component)objList[i]).gameObject);
                    return list7;
                case "Toggle":
                    List<Toggle> list8 = new List<Toggle>();
                    for (int i = 0; i < objList.Length; i++)
                        list8.Add(objList[i] as Toggle);
                    return list8;
            }
        }
        return list;
    }

    private static void IsGenerateEvent(GenerateInfo g)
    {
        switch (g.Field)
        {
            case "Button":
                g.EventStr = "onClick";
                g.IsGenerateEvent = true;
                break;
            case "Toggle":
                g.EventStr = "onValueChanged";
                g.IsGenerateEvent = true;
                break;
            case "UIButton":
                g.EventStr = "onClick";
                g.IsGenerateEvent = true;
                break;
            case "UIInput":
                g.EventStr = "onChange";
                g.IsGenerateEvent = true;
                break;
            case "UIToggle":
                g.EventStr = "onChange";
                g.IsGenerateEvent = true;
                break;
        }
    }

    public static bool IsHasAttribute(System.Type type)
    {
        System.Object[] oList = type.GetCustomAttributes(typeof(AAutoQuote), false);
        foreach (var item in oList)
        {
            if ((item as AAutoQuote) != null)
                return true;
        }

        return false;
    }

    public static bool IsSubClass<T>(System.Type type)
    {
        return typeof(T).IsAssignableFrom(type);
    }

    private static void GetNodePath(Transform trans, ref string path)
    {
        if (path == "")
        {
            path = trans.name;
        }
        else
        {
            path = trans.name + "/" + path;
        }

        if (trans.parent != null)
        {
            GetNodePath(trans.parent, ref path);
        }
    }

    public static string GetFiles(DirectoryInfo directory, string fileName, string pattern)
    {
        if (directory.Exists || pattern.Trim() != string.Empty)
        {

            foreach (FileInfo info in directory.GetFiles(pattern))
            {
                if (info.Name == fileName)
                {
                    result = info.FullName;
                    break;
                }
            }

            foreach (DirectoryInfo info in directory.GetDirectories())
            {
                GetFiles(info, fileName, pattern);
            }
        }
        string returnString = result;
        return returnString;

    }

    private static Transform FindEnd(Transform t, string name)
    {
        Transform tr = t.Find(name);
        if (tr == null)
        {
            var oList = t.GetComponentsInChildren(typeof(Transform), true);

            foreach (var node in oList)
            {
                if (node.name == name)
                {
                    tr = node.transform;
                    break;
                }
            }
        }

        return tr;
    }

}


public class GenerateInfo
{
    public bool IsGenerateEvent { get; set; }
    public string Field { get; set; }
    public bool IsArry { get; set; }
    public bool IsList { get; set; }

    public string LenStr { get; set; }
    public string EventStr { get; set; }

    public string FieldText { get; set; }


}

#endif